home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
BBS in a Box 7
/
BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso
/
Files
/
Tele
/
C
/
Comet2.1.3.cpt
/
Comet
/
tftp_util.c
< prev
next >
Wrap
Text File
|
1991-02-11
|
18KB
|
807 lines
/* Copyright 1984 by the Massachusetts Institute of Technology */
/*
Copyright Cornell University 1986. All rights are reserved.
As of 4/10/86:
This source file may have no changes from the M.I.T original
other than this notice; but it has been tested as part of
Cornell's Aztec-C port. See notice.h
*/
/* See permission and disclaimer notice in file "notice.h" */
#include <notice.h>
#include <em.h>
#include <h19.h>
#include "tftp.h"
#ifndef DUALTCP
/* declarations needed if MacTCP stands alone */
int (*tfs_alert)();
int (*tfs_done)();
char macfile[256]; /* for Macintosh file name translation */
int ntftps = 0;
int tfstate = OFF;
long refusedt = 0; /* time of most recent transfer refusal */
static unsigned socket = 0;
unsigned short udp_socket()
{
if (socket)
return socket++;
socket = cticks;
if (socket < 1000)
socket +=1000;
return(socket++);
}
#endif
extern unsigned NDEBUG;
#define MINTICKS 60 /* 1 second */
extern int (*tfs_alert)();
extern int (*tfs_done)();
extern char macfile[]; /* for Macintosh file name translation */
extern int tfstate;
extern long refusedt; /* time of most recent transfer refusal */
/* Utility routines */
/* Modified to eliminate dangerous resends, simplify round-trip
time estimation, identify old and extra connections and shut
them down, and use clock-tick timer, 12/83.
Modified to use standard error code for old TID's and to byte-
swap the error code. 12/23/83.
Modified to check foreign port number before considering
error packets to be meaningful. Also arranged to send
error packets back in the incoming packet buffer, to
avoid clobbering the current output packet, 1/84.
Initialization of new connections improved to set file descriptor
and udp connection pointers to null, to avoid errors on premature
cleanup, 1/16/84. <J.H.Saltzer>
1/24/84 - added octet mode.
<John Romkey>
1/2/85 - fixed length calculation bug in mtcp_tfudperr.
<John Romkey>
*/
extern char *malloc();
unsigned long getmyA5();
ip_addr p_host; /* source host of current packet */
unsigned short p_port; /* source port of current packet */
extern StreamPtr tfsstream;
extern StreamPtr tftpstream;
extern UDPiopb udpread;
/* the TCP Async Notification Routine */
pascal void udp_event(stream, eventCode, userDataPtr, icmpMsg)
StreamPtr stream;
short eventCode;
Ptr userDataPtr;
ICMPReport *icmpMsg;
{
unsigned long oldA5;
oldA5 = getmyA5();
switch (eventCode) {
case UDPDataArrival: {
#ifdef DATANOTIFY
if (stream == tftpstream)
tftpdata = TRUE;
else if (stream == tfsstream)
tfsdata = TRUE;
#endif
break;
}
case UDPICMPReceived: {
/* prerr25("UDP ICMP Receive"); */
break;
}
}
setA5(oldA5);
}
/* called from tk_yield if udpdata has arrived */
tftp_upcall()
{
int count = 0;
OSErr errcode;
if (tfsdata) {
/* it's on the TFTP request socket */
tfsdata = FALSE;
p_host = tfspb.csParam.receive.remoteHost;
p_port = tfspb.csParam.receive.remotePort;
mtcp_tfshnd(tfspb.csParam.receive.rcvBuff,
tfspb.csParam.receive.rcvBuffLen,
tfspb.csParam.receive.remoteHost,
tfspb.csParam.receive.remotePort,
tfspb.csParam.receive.secondTimeStamp
);
/* now return the buffer to UDP */
tfspb.csCode = UDPBfrReturn;
if (errcode = PBControl(&tfspb, (Boolean) FALSE))
error("no buffer return tfsread");
/* and do another read */
tfspb.csCode = UDPRead;
tfspb.ioCompletion = tfs_read_done;
tfspb.csParam.receive.timeOut = 0;
if (errcode = PBControl(&tfspb, (Boolean) TRUE))
error("no tfsread");
tfs_read_req++;
}
if (tftpdata) {
/* data on the TFTP transfer socket */
tftpdata = FALSE;
p_host = tftppb.csParam.receive.remoteHost;
p_port = tftppb.csParam.receive.remotePort;
mtcp_tftprcv(tftppb.csParam.receive.rcvBuff,
tftppb.csParam.receive.rcvBuffLen,
tftppb.csParam.receive.remoteHost,
tftppb.csParam.receive.remotePort,
&tftpconn
);
if (tftpconn.tf_state != DEAD) {
/* if transfer died/finished don't do another read! */
/* now return the buffer to UDP */
tftppb.csCode = UDPBfrReturn;
if (errcode = PBControl(&tftppb, (Boolean) FALSE))
error("no buffer return tftpread");
/* and do another read */
tftppb.csCode = UDPRead;
tftppb.ioCompletion = tftp_read_done;
tftppb.csParam.receive.timeOut = 0;
if (errcode = PBControl(&tftppb, (Boolean) TRUE)) {
error("no tftpread");
}
tftp_read_req++;
}
}
}
/* write a tftp packet */
mtcp_tf_write(cn, len)
register struct tfconn *cn;
unsigned len;
{
register struct tfack *packet;
unsigned res;
packet = cn->tf_outp;
if (packet->tf_op == RRQ || packet->tf_op == WRQ) {
cn->tf_tries = REQTRIES;
}
else
cn->tf_tries = TFTPTRIES;
cn->tf_lastlen = len;
cn->tf_snt++;
res = (macudp_write(cn, packet, len) == 0) ? -1 : 0;
tm_tset((int) cn->tf_rt, mtcp_tftptmo, cn, cn->tf_tm);
cn->tf_sent = cticks;
cn->tf_NR = 1;
}
/* Dump a connection block for debugging purposes. */
#ifdef TFTPDEBUG
tfcndump(cn)
register struct tfconn *cn;
{
printf("Connection addr = %04x\n", cn);
printf("lastlen = %u\texpected = %u\n",
cn->tf_lastlen, cn->tf_expected);
printf("state = %u\tdir = %u\tmode = %u\n",
cn->tf_state, cn->tf_dir, cn->tf_mode);
printf("sent = %u\trcvd = %u\tous = %u\tmo = %u\trsnd = %u\n\n",
cn->tf_snt, cn->tf_rcv, cn->tf_ous,cn->tf_ntmo, cn->tf_rsnd);
printf("round trip delay = %U\tK = %u\tcurnt tmo = %U\n",
cn->tf_trt, cn->tf_K, cn->tf_rt);
printf("size = %U\n", cn->tf_size);
}
#endif
#ifdef DOLOG
/* Log a connection block.*/
tfcnlog(cn)
register struct tfconn *cn;
{
log(tftplog, "snt=%u rcvd=%u ous=%u tmo=%u rsnd=%u trt=%U",
cn->tf_snt, cn->tf_rcv, cn->tf_ous, cn->tf_ntmo, cn->tf_rsnd, cn->tf_trt);
}
#endif
/* patch the cn structure host/port to send a packet to a host w/ no connection */
mtcp_tfrpyerr(cn, code, text)
struct tfconn *cn;
unsigned code;
char *text;
{
ip_addr savehost;
unsigned short saveport;
savehost = cn->tf_host;
saveport = cn->tf_port;
cn->tf_host = p_host;
cn->tf_port = p_port;
mtcp_tfudperr(cn, code, text);
cn->tf_host = savehost;
cn->tf_port = saveport;
}
/* Send an error packet. If the code is nonzero, put it in the packet.
Otherwise, copy the text into the packet. */
char *errors[] = {
"The JNC Memorial BUGHALT",
"File not found",
"Access violation",
"Disk full",
"Illegal TFTP operation",
"Unknown transfer ID",
"File already exists",
"No such user"
};
mtcp_tfudperr(cn, code, text)
struct tfconn *cn;
unsigned code;
char *text;
{
unsigned len;
struct tferr perr;
len = 4;
perr.tf_op = ERROR;
perr.tf_code = code;
if (code == ERRTXT && text != NULL) {
strncpy(&perr.tf_err[0], text, TFERRLEN);
len += strlen(text) + 1;
}
else {
strncpy(&perr.tf_err[0], errors[code], TFERRLEN);
len += strlen(errors[code]) + 1;
}
#ifdef TFTPDEBUG
if (NDEBUG & APTRACE)
printf("TFTP: Sending error packet, code %u, <%s>\n",
code, perr.tf_err);
#endif
return(macudp_write(cn, &perr, len));
}
/* Cleanup routine called when done */
long mtcp_tfcleanup(cn)
struct tfconn *cn;
{
long size;
#ifdef TFTPDEBUG
if (NDEBUG & INFOMSG)
printf("TFCLEANUP called\n");
#endif
if (cn->tf_mode != TEST)
fclose(cn->tf_fd);
macudp_close(cn);
tm_clear(cn->tf_tm);
tm_free(cn->tf_tm);
cn->tf_tm = NULL;
free(cn->tf_outp);
cn->tf_outp = NULL;
cn->tf_state = DEAD;
/* tfcndump(cn); */
#ifdef DOLOG
tfcnlog(cn);
#endif
size = cn->tf_size;
return(size);
}
macudp_write(cn, packet, len)
struct tfconn * cn;
char * packet;
int len;
{
UDPiopb wpb;
OSErr errcode;
struct trash {
wdsEntry udp;
int term;
} wds;
wds.udp.length = len;
wds.udp.ptr = packet;
wds.term = 0;
wpb.ioCRefNum = ipp_refnum;
wpb.csCode = UDPWrite;
wpb.udpStream = cn->stream;
wpb.csParam.send.remoteHost = cn->tf_host;
wpb.csParam.send.remotePort = cn->tf_port;
wpb.csParam.send.wdsPtr = &wds;
wpb.csParam.send.checkSum = (Boolean) TRUE;
if (errcode = PBControl(&wpb, (Boolean) FALSE)) {
error("macudp_write failed");
}
}
macudp_close(cn)
struct tfconn *cn;
{
UDPiopb cpb;
cpb.ioCRefNum = ipp_refnum;
cpb.csCode = UDPRelease;
cpb.udpStream = cn->stream;
if (PBControl(&cpb, (Boolean) FALSE))
error("MacTCP UDP release failed");
}
/* Handle an incoming ack */
mtcp_tfdoack(cn, ack, len)
register struct tfconn *cn;
register struct tfack *ack;
unsigned len;
{
if (ack->tf_block == cn->tf_expected) {
tf_rtt(cn);
tm_clear(cn->tf_tm);
cn->tf_state = RCVACK;
tfssend(cn);
}
else {
/* We have received an ACK,
but not for the data block we sent. It must be for
a duplicate, since we wouldn't have sent
the current data block if we hadn't gotten an ACK for
the previous one. This duplicate ACK means either
that the network resent a packet that it wasn't sure
got through, or else the other end resent the ACK
because our current data block is lost or late.
In either case, we can safely ignore this extra ACK,
and if the ACK we want doesn't come our own timer will
get us started again. It isn't safe
to resend the current data block now unless we are
absolutely certain that the other end won't reack
it if the earlier send was just delayed.
*/
cn->tf_ous++;
#ifdef TFTPDEBUG
if (NDEBUG & APTRACE)
printf("TFTP: ACK for block %u received again.\n",
ack->tf_block);
#endif
}
}
/* ack a certain block number */
mtcp_tfsndack(cn, number)
register struct tfconn *cn;
unsigned number;
{
register struct tfack *pack;
pack = (struct tfack *) cn->tf_outp;
cn->tf_lastlen = sizeof(struct tfack);
pack->tf_op = ACK;
pack->tf_block = number;
#ifdef TFTPDEBUG
if (NDEBUG & APTRACE)
printf("TFTP: ACKing block %u\n", number);
#endif
return(mtcp_tf_write(cn, sizeof(struct tfack)));
}
/* timeout has occured, retry or kill? */
mtcp_tftptmo(cn)
register struct tfconn *cn;
{
#ifdef TFTPDEBUG
if (NDEBUG & TMO)
printf("TFTP: Timeout.\n");
#endif
cn->tf_ntmo++;
if (--cn->tf_tries) {
/* do a(nother) retry */
#ifdef TFTPDEBUG
if (NDEBUG & APTRACE)
printf("TFTP: resending\n");
#endif
cn->tf_rsnd++;
cn->tf_NR++;
macudp_write(cn, cn->tf_outp, cn->tf_lastlen);
tm_clear(cn->tf_tm);
tm_tset((int) cn->tf_rt, mtcp_tftptmo, cn, cn->tf_tm);
}
else {
cn->tf_state = TIMEOUT;
if (cn->tf_dir == PUT)
tfssend(cn);
else
tfsrcv(cn);
}
}
mtcp_tfkill(cn)
register struct tfconn *cn;
{
tm_clear(cn->tf_tm);
cn->tf_state = DEAD;
if (cn->tf_dir == PUT)
tfsrcv(cn);
else
tfssend(cn);
}
/* Process a data packet received for TFTP connection cn, according to the
type (ASCII, IMAGE, TEST) specified in the connection block. Also
handle out of secquence blocks and check on block length. If a block
is way to short (len < tftp header size) send back an error message
and abort the transfer; we have CSR disease. If the block is less
than 512 bytes long, shut down the transfer; we're done.
Otherwise, just write it to disk (if necessary).
*/
mtcp_tfdodata(cn, ptfdat, len)
register struct tfconn *cn;
struct tfdata *ptfdat;
unsigned len;
{
register char *data;
unsigned count;
static int crseen = FALSE;
extern char macfile[];
if (len < 4) {
mtcp_tfudperr(cn, ERRTXT, "You have CSR disease.");
/* rpy */
mtcp_tfkill(cn);
return(0);
}
len -= 4; /* BAD. */
if (ptfdat->tf_block != cn->tf_expected) {
/* We got a retransmission of a packet we have already tried to
ACK. If we retransmit the ACK, and the old ACK finally gets through also,
our correspondent will resend the next data block, and we will do the same
thing for it, on through to the end of the file. So we shouldn't retransmit
the ACK until we are convinced that the first one was actually lost. We will
become convinced if our own timeout waiting for the next data packet
expires.
Here is what you shouldn't do. . .
if (ptfdat->tf_block == cn->tf_expected-1)
mtcp_tfsndack(cn, ptfdat->tf_block);
And now we return to correct procedures. . .
*/
#ifdef TFTPDEBUG
if (NDEBUG & (INFOMSG|PROTERR|APTRACE))
printf("TFTP: Got block %u, expecting %u.\n",
ptfdat->tf_block, cn->tf_expected);
#endif
cn->tf_ous++;
return(0);
}
if (cn->tf_state != DATAWAIT) {
#ifdef TFTPDEBUG
printf("TFTP: Received unexpected data block tf_state is %d.\n",cn->tf_state);
#endif
mtcp_tfudperr(cn, ERRTXT, "Rcvd unexpected data block");
/* mtcp_tfrpyerr */
mtcp_tfkill(cn);
return(0);
}
tm_clear(cn->tf_tm);
tf_rtt(cn);
cn->tf_size += len;
data = ptfdat->tf_data;
if (cn->tf_mode == IMAGE || cn->tf_mode == TEST || cn->tf_mode == OCTET) {
if (cn->tf_mode != TEST && fwrite(data, 1, len, cn->tf_fd) != len)
{
mtcp_tfudperr(cn, DISKFULL, &macfile[0]);
/* printf("TFTP: disk is full!\n"); */
mtcp_tfkill(cn);
return(0);
}
}
else if (cn->tf_mode == ASCII) {
register char thechar;
for (count = len + 1; --count; ) {
if ( (thechar = *data++) ) {
if (thechar == LF) {
if (crseen) {
crseen = FALSE;
continue;
}
else
thechar = CR;
}
else if (thechar == CR) {
crseen = TRUE;
}
if (putc(thechar, cn->tf_fd) == EOF) {
mtcp_tfudperr(cn, DISKFULL, &macfile[0]);
/* printf("TFTP: disk is full!\n"); */
mtcp_tfkill(cn);
return(0);
}
}
}
}
else {
mtcp_tfudperr(cn, ERRTXT, "Internal error");
mtcp_tfkill(cn);
return(0);
}
if (len == NORMLEN)
cn->tf_state = RCVDATA;
else
cn->tf_state = RCVLASTDATA;
/* Send the ack AFTER writing the data */
mtcp_tfsndack(cn, ptfdat->tf_block);
cn->tf_expected++;
tfsrcv(cn);
}
/* recalculate round trip time and set vars */
tf_rtt(cn)
register struct tfconn *cn;
{
long trtM;
trtM = cticks - cn->tf_sent; /* Measured round trip time */
if (cn->tf_NR_last == 1)
cn->tf_K = Kinit;
if (cn->tf_NR == 1)
cn->tf_trt = (trtM + cn->tf_trt) / 2;
else {
if ((cn->tf_NR_last > 1) && (cn->tf_K > 1) )
cn->tf_K -= Kinc;
cn->tf_trt += cn->tf_trt / cn->tf_K;
}
cn->tf_rt = max(min(cn->tf_trt * TMMULT, MAXTMO), MINTICKS);
cn->tf_NR_last = cn->tf_NR;
}
/* Handle an incoming packet after a connection has been opened */
mtcp_tftprcv(pdata, len, fhost, port, cn)
struct tfdata *pdata;
unsigned len;
ip_addr fhost;
unsigned short port;
struct tfconn *cn;
{
unsigned op;
cn->tf_rcv++;
op = pdata->tf_op;
pdata->tf_op = op;
switch (op) {
case DATA: {
if (mtcp_tfckport(cn, pdata, port))
mtcp_tfdodata(cn, pdata, len);
break;
}
case ACK: {
if (mtcp_tfckport(cn, pdata, port))
mtcp_tfdoack(cn, pdata, len);
break;
}
case ERROR: {
if ((cn->tf_haveport == 0) ||
(cn->tf_port == port)) {
mtcp_tfdoerr(cn, pdata, len);
mtcp_tfkill(cn);
}
#ifdef TFTPDEBUG
else if (NDEBUG & ROUTE) {
printf("TFTP: ignoring error packet.\n");
mtcp_tfdoerr(cn, pdata, len);
}
#endif
break;
}
default: {
error("mtcp_tftprcv: Got bad opcode %u", op);
mtcp_tfudperr(cn, ILLTFTP, " ");
mtcp_tfkill(cn);
break;
}
}
return(0);
}
mtcp_tfsndata(cn, len)
register struct tfconn *cn;
unsigned len;
{
register struct tfdata *tfdata;
tfdata = cn->tf_outp;
tfdata->tf_op = DATA;
tfdata->tf_block = cn->tf_expected;
#ifdef DEBUG
if (NDEBUG & APTRACE)
printf("TFTP: sending block %u\n", tfdata->tf_block);
#endif
return(mtcp_tf_write(cn, sizeof(struct tfdata) - 512 + len));
}
/* Setup a TFTP connection block. */
mttfmkcn(cn, dir, mode)
register struct tfconn * cn;
unsigned dir;
unsigned mode;
{
cn->tf_fd = NULL;
cn->tf_udp = NULL;
cn->tf_rcv = NULL;
cn->tf_snt = 0;
cn->tf_ous = 0;
cn->tf_ntmo = 0;
cn->tf_rsnd = 0;
cn->tf_dir = dir;
cn->tf_mode = mode;
cn->tf_size = 0L;
cn->tf_K = Kinit;
cn->tf_trt = T0;
cn->tf_rt = (long) min(cn->tf_trt * TMMULT, MAXTMO);
cn->tf_NR = 0;
cn->tf_NR_last = 1;
if (cn->tf_tm == NULL) {
cn->tf_tm = tm_alloc();
if (cn->tf_tm == NULL) {
error("TFTP: Couldn't allocate timer");
return(-1);
}
}
if (cn->tf_outp == NULL) {
cn->tf_outp = malloc(NORMLEN);
if (cn->tf_outp == NULL) {
error("TFTP: Couldn't allocate output packet");
tm_free(cn->tf_tm);
cn->tf_tm = NULL;
return(-2);
}
}
return(0);
}
/* Check over the port in the incoming packet */
mtcp_tfckport(cn, pdata, pfport)
register struct tfconn *cn;
register struct tfdata *pdata;
unsigned short pfport;
{
if (cn->tf_haveport == 0) {
/* Foreign port not yet identified, save it. */
if (cn->tf_expected == pdata->tf_block) {
/* but only if this is a response to our request. */
cn->tf_haveport = TRUE;
cn->tf_port = pfport;
}
else {
#ifdef TFTPDEBUG
if (NDEBUG & (PROTERR|NETERR))
printf("TFTP: Received packet from old connection.\n");
printf(" Expected block %u, got block %u\n",
cn->tf_expected, pdata->tf_block);
#endif
mtcp_tfrpyerr(cn, ERRTXT, "Rcvd data on old connection");
/* rpy */
return(FALSE);
}
}
else if (cn->tf_port != pfport) {
#ifdef TFTPDEBUG
if (NDEBUG & (PROTERR|NETERR)) {
printf("TFTP: Rcvd pkt from port %04x, expect port %04x,\n",
pfport, cn->tf_port);
}
#endif
mtcp_tfrpyerr(cn, BADTID, " ");
/* rpy */
return(FALSE);
}
return(TRUE);
}
/* Process an incoming error packet */
mtcp_tfdoerr(cn, perr, len)
register struct tfconn *cn;
struct tferr * perr;
unsigned len;
{
char terror[100];
sprintf(terror, "TFTP: Error from host: \"%s\"", &perr->tf_err[0]);
error(terror);
}